package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/wxpay"
	"bgs/util"
	"context"
	"database/sql"
	"fmt"
)

// EnsureAccountInfo 确保门店的账户信息并返回
func (s *StoreServiceImp) EnsureAccountInfo(ctx context.Context, q *sqlc.Queries, withLock bool, storeID int64) (storeAccountPtr *sqlc.StoreAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	var sqlErr error
	var storeAccount sqlc.StoreAccount
	if withLock {
		storeAccount, sqlErr = q.GetStoreAccountByStoreIDToLock(ctx, storeID)
	} else {
		storeAccount, sqlErr = q.GetStoreAccountByStoreID(ctx, storeID)
	}

	if sqlErr != nil {
		if sql.ErrNoRows == sqlErr {
			storeAccount, err = q.UpsertStoreAccount(ctx, sqlc.UpsertStoreAccountParams{
				StoreID:           storeID,
				AvailableAmount:   0,
				PendingAmount:     0,
				WithdrawingAmount: 0,
				WithdrawedAmount:  0,
			})
			if err != nil {
				return
			}
		} else {
			err = sqlErr
			return
		}
	}

	storeAccountPtr = &storeAccount
	slog.Infof("确保门店的账户信息并返回 ok")
	return
}

// SubmitAccountWithdrawApplymentParam 提交门店提现申请参数
type SubmitAccountWithdrawApplymentParam struct {
	StoreID  int64
	Target   sqlc.EmWithdrawTarget
	Amount   int64
	Remark   string
	RealName string
	IdCardNo string
}

// SubmitAccountWithdrawApplyment 提交门店提现申请
func (s *StoreServiceImp) SubmitAccountWithdrawApplyment(ctx context.Context, q *sqlc.Queries, p SubmitAccountWithdrawApplymentParam) (storeAccountWithdrawApplymentPtr *sqlc.StoreAccountWithdrawApplyment, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	isWithdrawToWechatChangePurse := p.Target == sqlc.EmWithdrawTargetWechatChangePurse
	if !isWithdrawToWechatChangePurse {
		err = fmt.Errorf("提现到银行卡暂时没实现")
		return
	}

	storeID := p.StoreID
	userID, err := s.GetStoreOwner(ctx, q, storeID)
	if err != nil {
		return
	}
	user, err := q.GetUser(ctx, userID)
	if err != nil {
		return
	}

	// 如果门店的实名和身份证不存在,则从入参取,如果存在忽略入参
	realName := user.RealName.String
	idCardNo := user.IDCardNo.String
	if user.RealName.String != p.RealName {
		if p.RealName == "" {
			err = fmt.Errorf("门店提现必须填写实名")
			return
		}
		user, err = q.UpdateUserRealName(ctx, sqlc.UpdateUserRealNameParams{
			ID:       user.ID,
			RealName: sqlc.NSFromString(p.RealName),
		})
		if err != nil {
			return
		}
		realName = user.RealName.String
	}
	if user.IDCardNo.String != p.RealName {
		if p.IdCardNo == "" {
			err = fmt.Errorf("门店提现必须填写身份证")
			return
		}
		user, err = q.UpdateUserIDCardNo(ctx, sqlc.UpdateUserIDCardNoParams{
			ID:       user.ID,
			IDCardNo: sqlc.NSFromString(p.IdCardNo),
		})
		if err != nil {
			return
		}
		idCardNo = user.IDCardNo.String
	}

	storeAccountWithdrawApplyment, err := q.CreateStoreAccountWithdrawApplyment(ctx, sqlc.CreateStoreAccountWithdrawApplymentParams{
		StoreID:    storeID,
		Target:     string(p.Target),
		AppID:      s.wechatVars.MinaAppID, //s.vars.AppID,
		MchID:      s.vars.MchIDAsProfit,
		Amount:     p.Amount,
		Remark:     p.Remark,
		UserID:     userID,
		OpenID:     user.OpenID,
		UserName:   realName,
		UserIDCard: sqlc.NSFromString(idCardNo),
	})
	if err != nil {
		return
	}
	slog.Infof("2 创建门店提现申请 ok")
	storeAccountWithdrawApplymentPtr = &storeAccountWithdrawApplyment
	outDetailNo, err := s.transferApply(ctx, storeAccountWithdrawApplymentPtr)
	if err != nil {
		return
	}
	slog.Infof("3 提交门店转账申请[outDetailNo:%s] ok", outDetailNo)

	storeAccountWithdrawApplyment, err = q.UpdateStoreAccountWithdrawApplymentOutDetailNo(ctx, sqlc.UpdateStoreAccountWithdrawApplymentOutDetailNoParams{
		ID:          storeAccountWithdrawApplymentPtr.ID,
		OutDetailNo: sqlc.NSFromString(outDetailNo),
	})
	if err != nil {
		slog.Errorf("UpdateUserAccountWithdrawApplymentOutDetailNo err:%v", err)
		return
	}
	slog.Infof("4 更新门店提现申请的转账详情单号 ok")

	storeAccountPtr, err := s.EnsureAccountInfo(ctx, q, true, userID)
	if err != nil {
		return
	}
	slog.Infof("5 确保门店账户记录 ok")

	_, err = s.UpdateAccountForStoreTransferRequestWithLock(ctx, q, UpdateAccountForStoreTransferWithLockParam{
		StoreAccountPtr: storeAccountPtr,
		WithdrawAmount:  p.Amount,
	})
	if err != nil {
		return
	}
	slog.Infof("5 门店总账记账=>提现申请 ok")

	return
}

// modifySettlementAccount wxpay 提交转账申请
func (s *StoreServiceImp) transferApply(ctx context.Context, storeAccountWithdrawApplymentPtr *sqlc.StoreAccountWithdrawApplyment) (outDetailNo string, err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	bizNo := fmt.Sprintf("STO:%d", storeAccountWithdrawApplymentPtr.ID)
	req := &wxpay.TransferApplyRequest{
		AppId:      storeAccountWithdrawApplymentPtr.AppID,
		MchId:      storeAccountWithdrawApplymentPtr.MchID,
		Amount:     storeAccountWithdrawApplymentPtr.Amount,
		Remark:     storeAccountWithdrawApplymentPtr.Remark,
		OpenId:     storeAccountWithdrawApplymentPtr.OpenID,
		UserName:   storeAccountWithdrawApplymentPtr.UserName,
		UserIdCard: storeAccountWithdrawApplymentPtr.UserIDCard.String,
		BizNo:      bizNo,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	reply, err := s.proxy.WxpayCl.TransferApply(ctxWithTimout, req)
	if err != nil {
		return
	}
	outDetailNo = reply.OutDetailNo
	return
}

type BatchUpdateAccountWithdrawApplymentApplyStateParam struct {
	IDs        []int64
	ApplyState string
}

// BatchUpdateAccountWithdrawApplymentApplyState 批量更新门店提现状态
func (s *StoreServiceImp) BatchUpdateAccountWithdrawApplymentApplyState(ctx context.Context, q *sqlc.Queries, p BatchUpdateAccountWithdrawApplymentApplyStateParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	err = q.BatchUpdateStoreAccountWithdrawApplymentApplyState(ctx, sqlc.BatchUpdateStoreAccountWithdrawApplymentApplyStateParams{
		Column1:    p.IDs,
		ApplyState: p.ApplyState,
	})

	slog.Infof("批量更新门店提现状态=>%s成功", p.ApplyState)
	return
}

/****************************************************************************************/
/*                            更新门店总账                                       */
/****************************************************************************************/

// UpdateAccountForStoreTransferWithLockParam 更新门店总账(门店转账(提现)申请)参数
type UpdateAccountForStoreTransferWithLockParam struct {
	StoreAccountPtr *sqlc.StoreAccount
	WithdrawAmount  int64 // >0
}

// UpdateAccountForUserTransferRequestWithLock 更新门店总账(门店转账(提现)申请)
func (s *StoreServiceImp) UpdateAccountForStoreTransferRequestWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForStoreTransferWithLockParam) (storeAccountPtr *sqlc.StoreAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	storeAccount, err := q.UpdateStoreAccountSubtractAvailableAmountToAddWithdrawingAmount(ctx, sqlc.UpdateStoreAccountSubtractAvailableAmountToAddWithdrawingAmountParams{
		StoreID:         p.StoreAccountPtr.StoreID,
		AvailableAmount: p.WithdrawAmount,
	})
	if err != nil {
		return
	}
	storeAccountPtr = &storeAccount
	slog.Infof("更新门店总账(门店转账(提现)申请) ok")
	return
}

// UpdateAccountForUserTransferSuccessWithLock 更新门店总账(门店转账(提现)成功)
func (s *StoreServiceImp) UpdateAccountForUserTransferSuccessWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForStoreTransferWithLockParam) (storeAccountPtr *sqlc.StoreAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	storeAccount, err := q.UpdateStoreAccountSubtractWithdrawingAmountToAddWithdrawedAmount(ctx, sqlc.UpdateStoreAccountSubtractWithdrawingAmountToAddWithdrawedAmountParams{
		StoreID:           p.StoreAccountPtr.StoreID,
		WithdrawingAmount: p.WithdrawAmount,
	})
	if err != nil {
		return
	}
	storeAccountPtr = &storeAccount
	slog.Infof("更新门店总账(门店转账(提现)成功) ok")
	return
}

// UpdateAccountForUserTransferFailWithLock 更新门店总账(门店转账(提现)失败)
func (s *StoreServiceImp) UpdateAccountForUserTransferFailWithLock(ctx context.Context, q *sqlc.Queries, p UpdateAccountForStoreTransferWithLockParam) (storeAccountPtr *sqlc.StoreAccount, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	storeAccount, err := q.UpdateStoreAccountSubtractWithdrawingAmountToAddAvailableAmount(ctx, sqlc.UpdateStoreAccountSubtractWithdrawingAmountToAddAvailableAmountParams{
		StoreID:           p.StoreAccountPtr.StoreID,
		WithdrawingAmount: p.WithdrawAmount,
	})
	if err != nil {
		return
	}
	storeAccountPtr = &storeAccount
	slog.Infof("更新门店总账(门店转账(提现)失败) ok")
	return
}
